// 19.3 枚举类型
/**
 * 枚举类型（enumeration）使我们可以将一组整型常量组织在一起。和类一样，每个枚举类型定义了一种新的类型。枚举属于字面值常量类型（参见7.5.6节，第267页）。
 * C++包含两种枚举：限定作用域的和不限定作用域的。C++11新标准引入了限定作用域的枚举类型（scoped enumeration）。
 * 定义限定作用域的枚举类型的一般形式是：首先是关键字enum class（或者等价地使用enum struct），随后是枚举类型名字以及用花括号括起来的以逗号分隔的枚举成员（enumerator）列表，最后是一个分号：[插图]
 *  enum class open_modes { input, output, append };
 * 定义不限定作用域的枚举类型（unscoped enumeration）时省略掉关键字class（或struct），枚举类型的名字是可选的：[插图]
 *  enum color { red, yellow, green }; // 不限定作用域的枚举类型
 *  enum { floatPrec=6, doublePrec=10, double_doublePrec=15 }; // 未命名的、不限定作用域的枚举类型
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;
#include <functional>
#include "../VisualStudio2012/15/Quote.h"
#include "../VisualStudio2012/12/TextQuery.h"
#include <tuple>
#include <bitset>
#include <regex>
#include <random>
#include <cmath>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <typeinfo>

int main()
{
    // 枚举成员
    /*在限定作用域的枚举类型中，枚举成员的名字遵循常规的作用域准则，并且在枚举类型的作用域外是不可访问的。与之相反，在不限定作用域的枚举类型中，枚举成员的作用域与枚举类型本身的作用域相同：[插图]
enum color { red, yellow, green };         // 不限定作用域的枚举类型
enum stoplight { red, yellow, green };     // 错误：重复定义了枚举成员
enum class peppers { red, yellow, green }; // 正确：枚举成员被隐藏了
color eyes = green; // 正确：不限定作用域的枚举类型的枚举成员位于有效的作用域中
peppers p = green;  // 错误：peppers的枚举成员不在有效的作用域中
                    // color::green在有效的作用域中，但是类型错误
color hair = color::red;   // 正确：允许显示地访问枚举成员
peppers p2 = peppers::red; // 正确：使用pappers的red
      枚举成员是const，因此在初始化枚举成员时提供的初始值必须是常量表达式（参见2.4.4节，第58页）。也就是说，每个枚举成员本身就是一条常量表达式，我们可以在任何需要常量表达式的地方使用枚举成员。
        例如，我们可以定义枚举类型的constexpr变量：[插图]
constexpr intTypes charbits = intTypes::charTyp;
      我们也可以将一个enum作为switch语句的表达式，而将枚举值作为case标签（参见5.3.2节，第160页）。出于同样的原因，我们还能将枚举类型作为一个非类型模板形参使用（参见16.1.1节，第580页）；
        或者在类的定义中初始化枚举类型的静态数据成员（参见7.6节，第270页）。
    */

    // 和类一样，枚举也定义新的类型
    /*只要enum有名字，我们就能定义并初始化该类型的成员。要想初始化enum对象或者为enum对象赋值，必须使用该类型的一个枚举成员或者该类型的另一个对象：[插图]
open_modes om = 2;      // 错误：2不属于类型open_modes
om = open_modes::input; // 正确：input是open_modes的一个枚举成员
      一个不限定作用域的枚举类型的对象或枚举成员自动地转换成整型。因此，我们可以在任何需要整型值的地方使用它们：[插图]
int i = color::red;   // 正确：不限定作用域的枚举类型的枚举成员隐式地转换成int
int j = peppers::red; // 错误：限定作用域的枚举类型不会进行隐式转换
    */

    // 指定enum的大小
    /*尽管每个enum都定义了唯一的类型，但实际上enum是由某种整数类型表示的。在C++11新标准中，我们可以在enum的名字后加上冒号以及我们想在该enum中使用的类型：[插图]
enum intValues : unsigned long lonog {
  charTyp = 255, shortTyp = 65535, longTyp = 4294967295UL, long_longTyp = 18446744073709551615ULL
};
      如果我们没有指定enum的潜在类型，则默认情况下限定作用域的enum成员类型是int。对于不限定作用域的枚举类型来说，其枚举成员不存在默认类型，我们只知道成员的潜在类型足够大，肯定能够容纳枚举值。
      指定enum潜在类型的能力使得我们可以控制不同实现环境中使用的类型，我们将可以确保在一种实现环境中编译通过的程序所生成的代码与其他实现环境中生成的代码一致。
    */

    // 枚举类型的前置声明
    /*在C++11新标准中，我们可以提前声明enum。enum的前置声明（无论隐式地还是显示地）必须指定其成员的大小：[插图]
enum intValues : unsigned long long; // 不限定作用域的，必须指定成员类型
enum class open_modes;               // 限定作用域的枚举类型可以使用默认成员类型int
    */

    // 形参匹配与枚举类型
    /*要想初始化一个enum对象，必须使用该enum类型的另一个对象或者它的一个枚举成员（参见19.3节，第737页）。因此，即使某个整型值恰好与枚举成员的值相等，它也不能作为函数的enum实参使用：[插图]
// 不限定作用域的枚举类型，潜在类型因机器而异
enum Tokens { INLINE=128, VIRTUAL=129 };
void ff(Tokens);
void ff(int);
int main() {
  Tokens curTok = INLINE;
  ff(128);      // 精确匹配ff(int)
  ff(INLINE);   // 精确匹配ff(Tokens)
  ff(curTok);   // 精确匹配ff(Tokens)
  return 0;
}
      尽管我们不能直接将整型值传给enum形参，但是可以将一个不限定作用域的枚举类型的对象或枚举成员传给整型形参。此时，enum的值提升成int或更大的整型，实际提升的结果由枚举类型的潜在类型决定：[插图]
    */

    return 0;
}